package com.mlethe.library.calendarview;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import java.lang.reflect.Constructor;
import java.util.List;

/**
 * 月份切换ViewPager，自定义适应高度
 *
 * @Author Mlethe
 * @Date 2022/12/29
 */
class MonthViewPager extends ViewPager {

    CalendarViewDelegate mDelegate;

    private int mNextViewHeight, mPreViewHeight, mCurrentViewHeight;

    private int mMonthCount;

    private boolean isUpdateMonthView;

    /**
     * 是否使用滚动到某一天
     */
    private boolean isUsingScrollToCalendar = false;

    /**
     * 触屏偏差
     */
    protected final int mTouchSlop;

    /**
     * 开始位置
     */
    private float startX = -1f;
    /**
     * 是否回调
     */
    private boolean isCallback = false;

    private final OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            int height;
            if (position < getCurrentItem()) {//右滑-1
                height = (int) ((mPreViewHeight)
                        * (1 - positionOffset) +
                        mCurrentViewHeight
                                * positionOffset);
            } else {//左滑+！
                height = (int) ((mCurrentViewHeight)
                        * (1 - positionOffset) +
                        (mNextViewHeight)
                                * positionOffset);
            }
            ViewGroup.LayoutParams params = getLayoutParams();
            params.height = height;
            setLayoutParams(params);
        }

        @Override
        public void onPageSelected(int position) {
            int year = (position + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
            int month = (position + mDelegate.getMinYearMonth() - 1) % 12 + 1;
            // 月份改变事件
            if (mDelegate.mMonthChangeListener != null) {
                mDelegate.mMonthChangeListener.onMonthChange(year, month);
            }
            int monthStartDay = mDelegate.getMonthStartDay();
            Calendar selectedCalendar = null;
            if (!isUpdateMonthView && !isUsingScrollToCalendar) {
                selectedCalendar = CalendarUtil.getSelectCalendar(year, month, monthStartDay, mDelegate.getCurrentDay(), mDelegate.getDefaultCalendarSelectMode(), mDelegate.getSelectedCalendar());
                if (selectedCalendar != null) {
                    mDelegate.setSelectedCalendar(selectedCalendar);
                }
            } else {
                selectedCalendar = mDelegate.getSelectedCalendar();
            }
            BaseMonthView view = findViewWithTag(position);
            if (view != null) {
                view.setSelectedCalendar(selectedCalendar);
                view.invalidate();
            }
            if (!isUsingScrollToCalendar) {
                if (mDelegate.isWeekSelector()) {
                    if (mDelegate.mWeekSelectListener != null && view instanceof WeekMonthView) {
                        List<Calendar> calendars = ((WeekMonthView) view).getSelectWeekCalendar();
                        if (calendars != null && !calendars.isEmpty()) {
                            mDelegate.mWeekSelectListener.onWeekSelect(calendars, false);
                        }
                    }
                } else {
                    if (mDelegate.mCalendarSelectListener != null && selectedCalendar != null) {
                        mDelegate.mCalendarSelectListener.onCalendarSelect(selectedCalendar, false);
                    }
                }
            }
            updateMonthViewHeight(year, month, monthStartDay);
            isUsingScrollToCalendar = false;
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    };

    public MonthViewPager(@NonNull Context context) {
        this(context, null);
    }

    public MonthViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (ev == null) return super.onTouchEvent(ev);
        try {
            if (ev.getPointerCount() > 1)
                return super.onTouchEvent(ev);
        } catch (Exception e) {
            e.printStackTrace();
        }
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_MOVE) {
            if (startX == -1f) {
                startX = ev.getRawX();
                isCallback = false;
            } else if (!isCallback) {
                float x = ev.getRawX();
                if (x - startX < -mTouchSlop) {
                    if (!canScrollHorizontally(1)) {
                        // 已滑动到最右侧
                        isCallback = true;
                        CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                        if (scrollOutRangeListener != null) {
                            scrollOutRangeListener.onScrolledToLastPage();
                        }
                    }
                } else if (x - startX > mTouchSlop) {
                    if (!canScrollHorizontally(-1)) {
                        // 已滑动到最左侧
                        isCallback = true;
                        CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                        if (scrollOutRangeListener != null) {
                            scrollOutRangeListener.onScrolledToFirstPage();
                        }
                    }
                }
            }
        } else if (action == MotionEvent.ACTION_UP) {
            if (!isCallback) {
                float x = ev.getRawX();
                if (x - startX < -mTouchSlop) {
                    if (!canScrollHorizontally(1)) {
                        // 已滑动到最右侧
                        CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                        if (scrollOutRangeListener != null) {
                            scrollOutRangeListener.onScrolledToLastPage();
                        }
                    }
                } else if (x - startX > mTouchSlop) {
                    if (!canScrollHorizontally(-1)) {
                        // 已滑动到最左侧
                        CalendarView.OnScrollOutRangeListener scrollOutRangeListener = mDelegate.mScrollOutRangeListener;
                        if (scrollOutRangeListener != null) {
                            scrollOutRangeListener.onScrolledToFirstPage();
                        }
                    }
                }
            }
            startX = -1f;
            isCallback = false;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 初始化
     *
     * @param delegate delegate
     */
    void setup(CalendarViewDelegate delegate) {
        this.mDelegate = delegate;
        updateMonthViewHeight(mDelegate.getCurrentYear(), mDelegate.getCurrentMonth(), mDelegate.getMonthStartDay());
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        mMonthCount = 12 * (mDelegate.getMaxYear() - mDelegate.getMinYear())
                - mDelegate.getMinYearMonth() + 1 +
                mDelegate.getMaxYearMonth();
        setOffscreenPageLimit(1);
        setAdapter(new MonthViewPagerAdapter());
        removeOnPageChangeListener(mOnPageChangeListener);
        addOnPageChangeListener(mOnPageChangeListener);
    }

    /**
     * 更新月视图的高度
     *
     * @param year          year
     * @param month         month
     * @param monthStartDay 月起始日
     */
    private void updateMonthViewHeight(int year, int month, int monthStartDay) {
        int calendarItemHeight = mDelegate.getCalendarItemHeight();
        int weekStart = mDelegate.getWeekStart();
        int mode = mDelegate.getMonthViewShowMode();
        mCurrentViewHeight = CalendarUtil.getMonthViewHeight(year, month, monthStartDay, calendarItemHeight, weekStart, mode);
        mPreViewHeight = CalendarUtil.getMonthViewHeight(year, month - 1, monthStartDay, calendarItemHeight, weekStart, mode);
        mNextViewHeight = CalendarUtil.getMonthViewHeight(year, month + 1, monthStartDay, calendarItemHeight, weekStart, mode);
        ViewGroup.LayoutParams params = getLayoutParams();
        params.height = mCurrentViewHeight;
        setLayoutParams(params);
    }

    /**
     * 设置月起始日
     *
     * @param monthStartDay 月起始日
     * @return 月份是否改变，true 改变，否则false
     */
    boolean setMonthStartDay(int monthStartDay) {
        int oldMonthStartDay = mDelegate.getMonthStartDay();
        if (!mDelegate.setMonthStartDay(monthStartDay)) {
            return false;
        }
        int position = getCurrentItem();
        int currentDay = CalendarUtil.getDay();
        int posDiff = 0;
        if (oldMonthStartDay > currentDay && monthStartDay <= currentDay) {
            posDiff = 1;
        } else if (oldMonthStartDay <= currentDay && monthStartDay > currentDay) {
            posDiff = -1;
        }
        int pos = position + posDiff;
        int year = (pos + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
        int month = (pos + mDelegate.getMinYearMonth() - 1) % 12 + 1;
        Calendar calendar = CalendarUtil.getSelectCalendar(year, month, monthStartDay, mDelegate.getCurrentDay(), mDelegate.getDefaultCalendarSelectMode(), mDelegate.getSelectedCalendar());
        if (calendar != null) {
            mDelegate.setSelectedCalendar(calendar);
        }
        boolean isChangeMonth = posDiff != 0;
        if (isChangeMonth) {
            setAdapter(new MonthViewPagerAdapter());
            isUsingScrollToCalendar = true;
            setCurrentItem(pos);
        } else {
            int count = getChildCount();
            for (int i = 0; i < count; i++) {
                View childView = getChildAt(i);
                if (childView instanceof BaseMonthView) {
                    BaseMonthView monthView = (BaseMonthView) childView;
                    monthView.updateMonthStartDay(monthStartDay);
                    monthView.invalidate();
                }
            }
        }
        updateMonthViewHeight(year, month, monthStartDay);
        if (mDelegate.mCalendarSelectListener != null && calendar != null) {
            mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
        }
        return isChangeMonth;
    }

    /**
     * 更新日历数据
     */
    void updateData() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateData();
                monthView.invalidate();
            }
        }
        int position = getCurrentItem();
        int year = (position + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
        int month = (position + mDelegate.getMinYearMonth() - 1) % 12 + 1;
        updateMonthViewHeight(year, month, mDelegate.getMonthStartDay());
    }

    /**
     * 更新当前日期，夜间过度的时候调用这个函数，一般不需要调用
     */
    void updateCurrentDate() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateCurrentDate();
                monthView.invalidate();
            }
        }
    }

    /**
     * 更新标记日期
     */
    void updateScheme() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateScheme();
                monthView.invalidate();
            }
        }
    }

    /**
     * 更新字体颜色大小
     */
    final void updateStyle() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateStyle();
                monthView.invalidate();
            }
        }
    }

    /**
     * 设置月视图item高度
     */
    final void updateItemHeight() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateItemHeight();
                monthView.invalidate();
            }
        }
        int position = getCurrentItem();
        int year = (position + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
        int month = (position + mDelegate.getMinYearMonth() - 1) % 12 + 1;
        updateMonthViewHeight(year, month, mDelegate.getMonthStartDay());
    }

    /**
     * 更新CalendarPadding
     */
    final void updateCalendarPadding() {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            childView.invalidate();
        }
    }

    /**
     * 滚动到指定日期
     *
     * @param year         年
     * @param month        月
     * @param day          日
     * @param smoothScroll 是否有动画
     */
    void scrollToCalendar(int year, int month, int day, boolean smoothScroll) {
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.set(year, month - 1, 1);
        int actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        int monthStartDay = Math.min(mDelegate.getMonthStartDay(), actualMaximum);
        day = Math.min(day, actualMaximum);
        int selectYear = year;
        int selectMonth = month;
        if (day < monthStartDay) {
            selectMonth = selectMonth - 1;
            if (selectMonth == 0) {
                selectYear = selectYear - 1;
                selectMonth = 12;
            }
        }
        cal.set(year, selectMonth, day);
        long curTime = cal.getTimeInMillis();
        cal.set(mDelegate.getMinYear(), mDelegate.getMinYearMonth() - 1, 1);
        cal.set(java.util.Calendar.HOUR_OF_DAY, 0);
        cal.set(java.util.Calendar.MINUTE, 0);
        cal.set(java.util.Calendar.SECOND, 0);
        cal.set(java.util.Calendar.MILLISECOND, 0);
        actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        int minDay = Math.min(monthStartDay, actualMaximum);
        cal.set(java.util.Calendar.DAY_OF_MONTH, minDay);
        long minTime = cal.getTimeInMillis();
        cal.set(mDelegate.getMaxYear(), mDelegate.getMaxYearMonth(), 1);
        actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        cal.set(java.util.Calendar.DAY_OF_MONTH, Math.min(monthStartDay, actualMaximum) - 1);
        cal.set(java.util.Calendar.HOUR_OF_DAY, 23);
        cal.set(java.util.Calendar.MINUTE, 59);
        cal.set(java.util.Calendar.SECOND, 59);
        cal.set(java.util.Calendar.MILLISECOND, 999);
        long maxTime = cal.getTimeInMillis();
        long diffMin = curTime - minTime;
        long diffMax = maxTime - curTime;
        if (diffMin < 0L) {
            year = mDelegate.getMinYear();
            month = mDelegate.getMinYearMonth();
            day = Math.max(day, minDay);
            selectYear = year;
            selectMonth = month;
        } else if (diffMax < 0L) {
            year = mDelegate.getMaxYear();
            month = mDelegate.getMaxYearMonth();
            day = Math.min(day, cal.get(java.util.Calendar.DAY_OF_MONTH));
            selectYear = year;
            selectMonth = month;
        }
        Calendar calendar = new Calendar();
        calendar.setYear(year);
        calendar.setMonth(month);
        calendar.setDay(day);
        calendar.setCurrentMonth(true);
        calendar.setCurrentDay(calendar.equals(mDelegate.getCurrentDay()));
        mDelegate.setSelectedCalendar(calendar);
        int y = selectYear - mDelegate.getMinYear();
        int position = 12 * y + selectMonth - mDelegate.getMinYearMonth();
        int curItem = getCurrentItem();
        if (curItem != position) {
            isUsingScrollToCalendar = true;
            setCurrentItem(position, smoothScroll);
        }

        BaseMonthView view = findViewWithTag(position);
        if (view != null) {
            view.setSelectedCalendar(calendar);
            view.invalidate();
        }
        if (mDelegate.isWeekSelector()) {
            if (mDelegate.mWeekSelectListener != null && view instanceof WeekMonthView) {
                List<Calendar> calendars = ((WeekMonthView) view).getSelectWeekCalendar();
                if (calendars != null && !calendars.isEmpty()) {
                    mDelegate.mWeekSelectListener.onWeekSelect(calendars, false);
                }
            }
        } else {
            if (mDelegate.mCalendarSelectListener != null) {
                mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
            }
        }
    }

    /**
     * 滚动到指定日期
     *
     * @param year         年
     * @param month        月
     * @param smoothScroll 是否有动画
     */
    void scrollToCalendar(int year, int month, boolean smoothScroll) {
        int selectYear = year;
        int selectMonth = month;
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.clear();
        cal.setTimeInMillis(System.currentTimeMillis());
        int day = cal.get(java.util.Calendar.DAY_OF_MONTH);
        int monthStartDay = mDelegate.getMonthStartDay();
        if (day < monthStartDay) {
            selectMonth = selectMonth + 1;
            if (selectMonth == 13) {
                selectYear = selectYear + 1;
                selectMonth = 1;
            }
        }
        cal.set(java.util.Calendar.YEAR, selectYear);
        cal.set(java.util.Calendar.DAY_OF_MONTH, 1);
        cal.set(java.util.Calendar.MONTH, selectMonth - 1);
        int actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        day = Math.min(day, actualMaximum);
        cal.set(java.util.Calendar.DAY_OF_MONTH, day);
        long curTime = cal.getTimeInMillis();
        cal.set(mDelegate.getMinYear(), mDelegate.getMinYearMonth() - 1, 1);
        cal.set(java.util.Calendar.HOUR_OF_DAY, 0);
        cal.set(java.util.Calendar.MINUTE, 0);
        cal.set(java.util.Calendar.SECOND, 0);
        cal.set(java.util.Calendar.MILLISECOND, 0);
        actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        int minDay = Math.min(monthStartDay, actualMaximum);
        cal.set(java.util.Calendar.DAY_OF_MONTH, minDay);
        long minTime = cal.getTimeInMillis();
        cal.set(mDelegate.getMaxYear(), mDelegate.getMaxYearMonth(), 1);
        actualMaximum = cal.getActualMaximum(java.util.Calendar.DATE);
        cal.set(java.util.Calendar.DAY_OF_MONTH, Math.min(monthStartDay, actualMaximum) - 1);
        cal.set(java.util.Calendar.HOUR_OF_DAY, 23);
        cal.set(java.util.Calendar.MINUTE, 59);
        cal.set(java.util.Calendar.SECOND, 59);
        cal.set(java.util.Calendar.MILLISECOND, 999);
        long maxTime = cal.getTimeInMillis();
        long diffMin = curTime - minTime;
        long diffMax = maxTime - curTime;
        if (diffMin < 0L) {
            year = mDelegate.getMinYear();
            month = mDelegate.getMinYearMonth();
        } else if (diffMax < 0L) {
            year = mDelegate.getMaxYear();
            month = mDelegate.getMaxYearMonth();
        }
        Calendar calendar = CalendarUtil.getSelectCalendar(year, month, monthStartDay, mDelegate.getCurrentDay(), mDelegate.getDefaultCalendarSelectMode(), mDelegate.getSelectedCalendar());
        if (calendar != null) {
            mDelegate.setSelectedCalendar(calendar);
        }
        int y = year - mDelegate.getMinYear();
        int position = 12 * y + month - mDelegate.getMinYearMonth();
        int curItem = getCurrentItem();
        if (curItem != position) {
            isUsingScrollToCalendar = true;
            setCurrentItem(position, smoothScroll);
        }

        BaseMonthView view = findViewWithTag(position);
        if (view != null) {
            view.setSelectedCalendar(calendar);
            view.invalidate();
        }
        if (mDelegate.isWeekSelector()) {
            if (mDelegate.mWeekSelectListener != null && view instanceof WeekMonthView) {
                List<Calendar> calendars = ((WeekMonthView) view).getSelectWeekCalendar();
                if (calendars != null && !calendars.isEmpty()) {
                    mDelegate.mWeekSelectListener.onWeekSelect(calendars, false);
                }
            }
        } else {
            if (mDelegate.mCalendarSelectListener != null && calendar != null) {
                mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
            }
        }
    }

    /**
     * 滚动到当前日期
     *
     * @param smoothScroll 是否有动画
     */
    void scrollToCurrent(boolean smoothScroll) {
        Calendar currentDay = mDelegate.getCurrentDay();
        scrollToCalendar(currentDay.getYear(), currentDay.getMonth(), currentDay.getDay(), smoothScroll);
    }

    /**
     * 更新日期范围
     */
    void updateRange() {
        isUpdateMonthView = true;
        notifyDataSetChanged();
        isUpdateMonthView = false;
        if (getVisibility() != VISIBLE) {
            return;
        }
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView instanceof BaseMonthView) {
                BaseMonthView monthView = (BaseMonthView) childView;
                monthView.updateSelectCalendar();
                monthView.invalidate();
            }
        }
        Calendar calendar = mDelegate.getSelectedCalendar();
        int year = calendar.getYear();
        int month = calendar.getMonth();
        if (calendar.getDay() < mDelegate.getMonthStartDay()) {
            month = month - 1;
            if (month == 0) {
                year = year - 1;
                month = 12;
            }
        }
        int y = year - mDelegate.getMinYear();
        int position = 12 * y + month - mDelegate.getMinYearMonth();
        int curItem = getCurrentItem();
        if (curItem != position) {
            isUsingScrollToCalendar = true;
            setCurrentItem(position, false);
        }
        if (mDelegate.mCalendarSelectListener != null) {
            mDelegate.mCalendarSelectListener.onCalendarSelect(calendar, false);
        }
    }

    /**
     * 刷新
     */
    private void notifyDataSetChanged() {
        mMonthCount = 12 * (mDelegate.getMaxYear() - mDelegate.getMinYear())
                - mDelegate.getMinYearMonth() + 1 +
                mDelegate.getMaxYearMonth();
        notifyAdapterDataSetChanged();
    }

    private void notifyAdapterDataSetChanged() {
        PagerAdapter adapter = getAdapter();
        if (adapter == null) {
            return;
        }
        adapter.notifyDataSetChanged();
    }

    /**
     * 获取月的数量
     *
     * @return
     */
    protected int getMonthCount() {
        return mMonthCount;
    }

    /**
     * 日历卡月份Adapter
     */
    private final class MonthViewPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return mMonthCount;
        }

        @Override
        public int getItemPosition(@NonNull Object object) {
            return isUpdateMonthView ? POSITION_NONE : super.getItemPosition(object);
        }

        @Override
        public boolean isViewFromObject(View view, @NonNull Object object) {
            return view.equals(object);
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            int year = (position + mDelegate.getMinYearMonth() - 1) / 12 + mDelegate.getMinYear();
            int month = (position + mDelegate.getMinYearMonth() - 1) % 12 + 1;
            BaseMonthView view;
            try {
                Constructor<?> constructor = mDelegate.getMonthViewClass().getConstructor(Context.class);
                view = (BaseMonthView) constructor.newInstance(getContext());
            } catch (Exception e) {
                e.printStackTrace();
                view = new DefaultMonthView(getContext());
            }
            view.setup(mDelegate);
            view.setTag(position);
            view.initMonthWithDate(year, month);
            view.setOnCalendarClickListener((calendar, pos) -> {
                int curItem = getCurrentItem();
                isUsingScrollToCalendar = true;
                setCurrentItem(pos < 7 ? curItem - 1 : curItem + 1);
            });
            container.addView(view);
            return view;
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            BaseMonthView view = (BaseMonthView) object;
            view.onDestroy();
            container.removeView(view);
        }
    }
}
